package com.witframework.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.util.Assert;

import com.witframework.ClassForNameException;
import com.witframework.core.BaseEntity;
import com.witframework.core.persistence.NotQueryField;


/**
 * Provide tools for the operation of bean
 * ?extends apache BeanUtils
 * @author westerasoft
 *
 */
public class BeanUtils {
	/**
	 * 返回一个包含某些 Field 对象的数组，这些Field对象的类型是由参数type指定的
	 * @param object -要获取字段的对象
	 * @param type -Field的类型
	 * @return 表示字段的 Field 对象的数组
	 */
	public static List<Field> getFieldsByType(Object object, Class type) {
		List<Field> list = new ArrayList<Field>();
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (field.getType().isAssignableFrom(type)) {
				list.add(field);
			}
		}
		return list;
	}
	/**
	 * <p>
	 * 返回一个 Field 对象，该对象反映此 Class 对象所表示的类或接口的指定已声明字段。</P><P>
	 * propertyName 参数是一个 String，它指定所需字段的简称。
	 * 如果参数propertyName指定的Field在超类中，则该方法可以返回超类中的Field。<br>
	 * 注意，此方法不反映数组类的 length 字段。</P>
	 * @param object -要获取字段的对象
	 * @param propertyName -字段名
	 * @return 此类或超类中指定字段的 Field 对象
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);
		return getDeclaredField(object.getClass(), propertyName);
	}
	/**
	 * @see #getDeclaredField(Object, String)
	 * @param clazz -类
	 * @param propertyName -字段名
	 * @return 此类或超类中指定字段的 Field 对象
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Class clazz, String propertyName) throws NoSuchFieldException {
		Assert.notNull(clazz);
		Assert.hasText(propertyName);
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(propertyName);
			} catch (NoSuchFieldException e) {
			}
		}
		throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
	}
	/**
	 * 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
	 * @param object -应该修改其字段的对象
	 * @param propertyName -字段名
	 * @param newValue -正被修改的 obj 的字段的新值
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public static void setDeclaredProperty(Object object, String propertyName,
			Object newValue) throws IllegalAccessException,
			NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);
		Field field = getDeclaredField(object, propertyName);
		boolean accessible = field.isAccessible();
		field.setAccessible(true);
		field.set(object, newValue);
		field.setAccessible(accessible);
	}
	/**
	 * 返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值，则自动将其包装在一个对象中。<br>
	 * 该方法将强制返回字段的值，而不管此 Field 对象是否强制实施 Java 语言访问控制或基础字段是不可访问的。<br>
	 * 如果基础字段是静态的，并且声明该字段的类尚未初始化，则初始化这个类。
	 * @param object - 从中提取所表示字段的值的对象
	 * @param propertyName -字段名
	 * @return 对象 obj 中的所表示字段的值；在返回之前，基值包装在一个适当的对象中
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public static Object getDeclaredProperty(Object object, String propertyName)
			throws IllegalAccessException, NoSuchFieldException {		
		try {
			return org.apache.commons.beanutils.PropertyUtils.getNestedProperty(object, propertyName);
		} catch (Exception e) {			
		}		
		Assert.notNull(object);
		Assert.hasText(propertyName);
		Field field = getDeclaredField(object, propertyName);
		
		boolean accessible = field.isAccessible();
		field.setAccessible(true);
		Object result = field.get(object);
		field.setAccessible(accessible);
		return result;
	}
	public static Object getDeclaredPropertyNoException(Object object, String propertyName){
		try {
			return getDeclaredProperty(object,propertyName);
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 *
	 *
	 * @param clazz The class to introspect
	 * @return the first generic declaration, or <code>Object.class</code> if cannot be determined
	 */
	public static Class getSuperClassGenricType(Class clazz) {
		return getSuperClassGenricType(clazz, 0);
	}

	/**
	 *
	 *
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration,start from 0.
	 * @return the index generic declaration, or <code>Object.class</code> if cannot be determined
	 */
	public static Class getSuperClassGenricType(Class clazz, int index) {
		Type genType = clazz.getGenericSuperclass();
		if (!(genType instanceof ParameterizedType)) {
			//log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length || index < 0) {
			//log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
			//		+ params.length);
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			//log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}
		return (Class) params[index];
	}

	public static Class getClassByName(String className){
		try {
			return Class.forName(className);
		} catch (ClassNotFoundException e) {
		}
		return null;
	}
	public static List<String> getFieldNames(Class clazz) {
		List fList = new ArrayList();
		Field[] fs = clazz.getDeclaredFields();
		for (Field f : fs) {
			fList.add(f.getName());
		}
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass
				.getSuperclass()) {
			try {
				fs = clazz.getDeclaredFields();
				for (Field f : fs) {
					fList.add(f.getName());
				}
			} catch (Exception e) {
			}
		}
		return fList;
	}
	public static List<String> getAllFieldNames(Class clazz,Class base) {
		List fList = new ArrayList();
		Field[] fs = clazz.getDeclaredFields();
		if(fList.size()>100) return fList;
		for (Field f : fs) {
			String name=clazz.getSimpleName()+"."+f.getName();
			if(!fList.contains(name))
				fList.add(name);
			if (base.isAssignableFrom(f.getType())&&!clazz.isAssignableFrom(f.getType())) {
				fList.addAll(getAllFieldNames(f.getType(),base));
			}
		}
		for (Class superClass = clazz.getSuperclass(); superClass!=null&&superClass != base; superClass = superClass.getSuperclass()) {
			try {
				fs = superClass.getDeclaredFields();
				for (Field f : fs) {
					String name=clazz.getSimpleName()+"."+f.getName();
					name=name.toLowerCase();
					if(!fList.contains(name))
						fList.add(name);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return fList;
	}
	public static List<Field> getFields(Class clazz) {
		List fList = new ArrayList();
		Field[] fs= clazz.getDeclaredFields();
		for (Field f : fs) {
			try {
			PropertyDescriptor pDescriptor=new PropertyDescriptor(f.getName(),clazz);
			if(pDescriptor.getWriteMethod()!=null&&pDescriptor.getReadMethod()!=null)
				fList.add(f);
			}catch(Exception e){}
		}
		return fList;
	}
	public static List<Field> getAllField(Class clazz,Class base) {
		List fList = new ArrayList();
		Field[] fs ;
		for (Class superClass = clazz.getSuperclass(); superClass!=null&&base.isAssignableFrom(superClass);superClass = superClass.getSuperclass()) {
			try {
				fs = superClass.getDeclaredFields();
				for (Field f : fs) {
					PropertyDescriptor pDescriptor=new PropertyDescriptor(f.getName(),superClass);
					if(pDescriptor.getWriteMethod()!=null&&pDescriptor.getReadMethod()!=null)
						fList.add(f);
				}
			} catch (Exception e) {
				//e.printStackTrace();
			}
		}
		fs = clazz.getDeclaredFields();
		for (Field f : fs) {
			try {
			PropertyDescriptor pDescriptor=new PropertyDescriptor(f.getName(),clazz);
			if(pDescriptor.getWriteMethod()!=null&&pDescriptor.getReadMethod()!=null)
				fList.add(f);
			}catch(Exception e){}
		}
		return fList;
	}
	public static List<String> getEntityFieldNames(Class clazz,Class base,String prefix){
		List fList = new ArrayList();
		Field[] fs = (Field[])ArrayExpander.merge(clazz.getDeclaredFields(),clazz.getFields());
		for (Field f : fs) {
			fList.add(prefix+f.getName());
			//if(f.getType()!=clazz)
			if(base.isAssignableFrom(f.getType())
					//处理关联的自身的对象
					&&StringTools.findMaxSameCount(prefix.split("\\."))<2){
				fList.addAll(getEntityFieldNames(f.getType(),base,prefix+f.getName()+"."));
			}
		}
		for (Class superClass = clazz.getSuperclass(); superClass != Object.class; superClass = superClass
				.getSuperclass()) {
			try {
				fs = superClass.getDeclaredFields();
				for (Field f : fs) {
					if(!fList.contains(f.getName()))
						fList.add(f.getName());
				}
			} catch (Exception e) {
			}
		}
		return fList;
	}
	public static List<String> getEntityFieldNameValue(Class clazz,Class base,String prefix){
		List fList = new ArrayList();
		Field[] fs = clazz.getDeclaredFields();
		for (Field f : fs) {

			fList.add(prefix+f.getName());
			if(base.isAssignableFrom(f.getType())){
				fList.addAll(getEntityFieldNames(f.getType(),base,prefix+f.getName()+"."));
			}
		}
		for (Class superClass = clazz.getSuperclass(); superClass != Object.class; superClass = superClass
				.getSuperclass()) {
			try {
				fs = superClass.getDeclaredFields();
				for (Field f : fs) {
					if(!fList.contains(f.getName()))
						fList.add(f.getName());
				}
			} catch (Exception e) {
			}
		}
		return fList;
	}
	/**
	 * 返回与带有给定字符串名的类或接口相关联的 Class 对象。
	 * @param className - 所需类的完全限定名。
	 * @return 具有指定名的类的 Class 对象。
	 * @throws ClassForNameException -如果Class.forName方法失败（如果链接失败 或 如果此方法所激发的初始化失败 或 如果找不到该类）
	 */
	public static Class<?> classForName(String className){
		try {
			return Class.forName(className);
		} catch (ClassNotFoundException e) {
			throw new ClassForNameException("",e);
		}
	}
	public static Object newInstance(String className){
		try {
			return classForName(className).newInstance();
		} catch (Exception e) {
			return null;
		}
	}
	public static Method getMethod(Object obj,String name ,Class<?>...parameterTypes){
		try {
			return obj.getClass().getMethod(name, parameterTypes);
		} catch (Exception e) {
			return null;
		}
	}

	public static Object convertValue(Class clazz,String fieldName, String propertyValue) {
		//Field[] fields = getEntityClass().getDeclaredFields();
		List<Field> fields=BeanUtils.getAllField(clazz, BaseEntity.class);
		for (Field field : fields) {
			if (field.getName().equals(fieldName)) {
				Class fieldType = field.getType();
				return convertValue(fieldType,propertyValue);
			}else{
				if( field.getType().getSuperclass() ==BaseEntity.class){
					Field[] subFields = field.getType().getDeclaredFields();
					for (Field subField : subFields) {
						if(subField.isAnnotationPresent(NotQueryField.class)) continue;
						Class subFieldType = subField.getType();
						String subFieldName = subField.getName();
						Class subClass =BeanUtils.getClassByName(subFieldType.getName());
						if (subClass != null&& !subFieldName.equals("id")) {
							String subEntityFieldName=field.getName() + "."+ subFieldName;
							if(subEntityFieldName.equals(fieldName))
								return convertValue(subFieldType,propertyValue);
						}
					}
				}
			}
		}
		return null;
	}
	private static Object convertValue(Class fieldType, String propertyValue) {
		try {
			if (fieldType == String.class) {
				return propertyValue;
			} else if (fieldType == Double.class
					|| fieldType == double.class) {
				return Double.valueOf(propertyValue);
			} else if (fieldType == Integer.class
					|| fieldType == int.class) {
				return Integer.valueOf(propertyValue);
			} else if (fieldType == Float.class
					|| fieldType == float.class) {
				return Float.valueOf(propertyValue);
			} else if (fieldType == Long.class
					|| fieldType == long.class) {
				return Long.valueOf(propertyValue);
			} else if (fieldType == java.util.Date.class) {
				return DateUtil.convertStringToDate(propertyValue);
			} else if (fieldType == java.sql.Timestamp.class) {
				return new TimestampConverter().convert(Date.class, propertyValue);
			}
		} catch (Exception e) {
			return null;
		}
		return null;
	}	
}
